summaryrefslogtreecommitdiffstats
path: root/include/asm-ppc/bitops.h
diff options
context:
space:
mode:
Diffstat (limited to 'include/asm-ppc/bitops.h')
-rw-r--r--include/asm-ppc/bitops.h178
1 files changed, 133 insertions, 45 deletions
diff --git a/include/asm-ppc/bitops.h b/include/asm-ppc/bitops.h
index 959f2b302..3b8a24575 100644
--- a/include/asm-ppc/bitops.h
+++ b/include/asm-ppc/bitops.h
@@ -3,67 +3,155 @@
#include <asm/system.h>
#include <asm/byteorder.h>
+#include <linux/kernel.h> /* for printk */
#define BIT(n) 1<<(n&0x1F)
typedef unsigned long BITFIELD;
-/* Set bit 'nr' in 32-bit quantity at address 'addr' where bit '0'
- * is in the highest of the four bytes and bit '31' is the high bit
- * within the first byte. powerpc is BIG-Endian. Unless noted otherwise
- * all bit-ops return 0 if bit was previously clear and != 0 otherwise.
+/*
+ * These are ifdef'd out here because using : "cc" as a constraing
+ * results in errors from gcc. -- Cort
*/
-extern __inline__ int set_bit(int nr, void * add)
+#if 0
+extern __inline__ int set_bit(int nr, void * addr)
{
- BITFIELD *addr = add;
- long mask,oldbit;
-#ifdef __KERNEL__
- int s = _disable_interrupts();
-#endif
- addr += nr >> 5;
- mask = BIT(nr);
- oldbit = (mask & *addr) != 0;
- *addr |= mask;
-#ifdef __KERNEL__
- _enable_interrupts(s);
-#endif
- return oldbit;
+ unsigned long old, t;
+ unsigned long mask = 1 << (nr & 0x1f);
+ unsigned long *p = ((unsigned long *)addr) + (nr >> 5);
+
+ if ((unsigned long)addr & 3)
+ printk("set_bit(%lx, %p)\n", nr, addr);
+
+ __asm__ __volatile__(
+ "1:lwarx %0,0,%3 \n\t"
+ "or %1,%0,%2 \n\t"
+ "stwcx. %1,0,%3 \n\t"
+ "bne 1b \n\t"
+ : "=&r" (old), "=&r" (t) /*, "=m" (*p)*/
+ : "r" (mask), "r" (p)
+ /*: "cc" */);
+
+n return (old & mask) != 0;
}
-extern __inline__ int change_bit(int nr, void *add)
+extern __inline__ unsigned long clear_bit(unsigned long nr, void *addr)
{
- BITFIELD *addr = add;
- int mask, retval;
-#ifdef __KERNEL__
- int s = _disable_interrupts();
-#endif
- addr += nr >> 5;
- mask = BIT(nr);
- retval = (mask & *addr) != 0;
- *addr ^= mask;
-#ifdef __KERNEL__
- _enable_interrupts(s);
-#endif
- return retval;
+ unsigned long old, t;
+ unsigned long mask = 1 << (nr & 0x1f);
+ unsigned long *p = ((unsigned long *)addr) + (nr >> 5);
+
+ if ((unsigned long)addr & 3)
+ printk("clear_bit(%lx, %p)\n", nr, addr);
+ __asm__ __volatile__("\n\
+1: lwarx %0,0,%3
+ andc %1,%0,%2
+ stwcx. %1,0,%3
+ bne 1b"
+ : "=&r" (old), "=&r" (t) /*, "=m" (*p)*/
+ : "r" (mask), "r" (p)
+ /*: "cc"*/);
+
+ return (old & mask) != 0;
}
-extern __inline__ int clear_bit(int nr, void *add)
+extern __inline__ unsigned long change_bit(unsigned long nr, void *addr)
{
- BITFIELD *addr = add;
- int mask, retval;
-#ifdef __KERNEL__
- int s = _disable_interrupts();
-#endif
- addr += nr >> 5;
- mask = BIT(nr);
- retval = (mask & *addr) != 0;
- *addr &= ~mask;
-#ifdef __KERNEL__
- _enable_interrupts(s);
+ unsigned long old, t;
+ unsigned long mask = 1 << (nr & 0x1f);
+ unsigned long *p = ((unsigned long *)addr) + (nr >> 5);
+
+ if ((unsigned long)addr & 3)
+ printk("change_bit(%lx, %p)\n", nr, addr);
+ __asm__ __volatile__("\n\
+1: lwarx %0,0,%3
+ xor %1,%0,%2
+ stwcx. %1,0,%3
+ bne 1b"
+ : "=&r" (old), "=&r" (t) /*, "=m" (*p)*/
+ : "r" (mask), "r" (p)
+ /*: "cc"*/);
+
+ return (old & mask) != 0;
+}
#endif
- return retval;
+
+extern __inline__ int ffz(unsigned int x)
+{
+ int n;
+
+ x = ~x & (x+1); /* set LS zero to 1, other bits to 0 */
+ __asm__ ("cntlzw %0,%1" : "=r" (n) : "r" (x));
+ return 31 - n;
}
+/*
+ * This implementation of find_{first,next}_zero_bit was stolen from
+ * Linus' asm-alpha/bitops.h.
+ */
+
+extern __inline__ unsigned long find_first_zero_bit(void * addr, unsigned long size)
+{
+ unsigned int * p = ((unsigned int *) addr);
+ unsigned int result = 0;
+ unsigned int tmp;
+
+ if (size == 0)
+ return 0;
+ while (size & ~31UL) {
+ if (~(tmp = *(p++)))
+ goto found_middle;
+ result += 32;
+ size -= 32;
+ }
+ if (!size)
+ return result;
+ tmp = *p;
+ tmp |= ~0UL << size;
+found_middle:
+ return result + ffz(tmp);
+}
+
+/*
+ * Find next zero bit in a bitmap reasonably efficiently..
+ */
+extern __inline__ unsigned long find_next_zero_bit(void * addr, unsigned long size,
+ unsigned long offset)
+{
+ unsigned int * p = ((unsigned int *) addr) + (offset >> 5);
+ unsigned int result = offset & ~31UL;
+ unsigned int tmp;
+
+ if (offset >= size)
+ return size;
+ size -= result;
+ offset &= 31UL;
+ if (offset) {
+ tmp = *(p++);
+ tmp |= ~0UL >> (32-offset);
+ if (size < 32)
+ goto found_first;
+ if (~tmp)
+ goto found_middle;
+ size -= 32;
+ result += 32;
+ }
+ while (size & ~31UL) {
+ if (~(tmp = *(p++)))
+ goto found_middle;
+ result += 32;
+ size -= 32;
+ }
+ if (!size)
+ return result;
+ tmp = *p;
+found_first:
+ tmp |= ~0UL << size;
+found_middle:
+ return result + ffz(tmp);
+}
+
+
#define _EXT2_HAVE_ASM_BITOPS_
#define ext2_find_first_zero_bit(addr, size) \
ext2_find_next_zero_bit((addr), (size), 0)