2005-04-17 00:20:36 +02:00
|
|
|
/*
|
2013-09-18 11:45:36 +02:00
|
|
|
* Copyright IBM Corp. 1999,2013
|
2005-04-17 00:20:36 +02:00
|
|
|
*
|
2013-09-18 11:45:36 +02:00
|
|
|
* Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>,
|
|
|
|
*
|
|
|
|
* The description below was taken in large parts from the powerpc
|
|
|
|
* bitops header file:
|
|
|
|
* Within a word, bits are numbered LSB first. Lot's of places make
|
|
|
|
* this assumption by directly testing bits with (val & (1<<nr)).
|
|
|
|
* This can cause confusion for large (> 1 word) bitmaps on a
|
|
|
|
* big-endian system because, unlike little endian, the number of each
|
|
|
|
* bit depends on the word size.
|
|
|
|
*
|
|
|
|
* The bitop functions are defined to work on unsigned longs, so for an
|
|
|
|
* s390x system the bits end up numbered:
|
|
|
|
* |63..............0|127............64|191...........128|255...........196|
|
|
|
|
* and on s390:
|
|
|
|
* |31.....0|63....31|95....64|127...96|159..128|191..160|223..192|255..224|
|
|
|
|
*
|
|
|
|
* There are a few little-endian macros used mostly for filesystem
|
|
|
|
* bitmaps, these work on similar bit arrays layouts, but
|
|
|
|
* byte-oriented:
|
|
|
|
* |7...0|15...8|23...16|31...24|39...32|47...40|55...48|63...56|
|
|
|
|
*
|
|
|
|
* The main difference is that bit 3-5 (64b) or 3-4 (32b) in the bit
|
|
|
|
* number field needs to be reversed compared to the big-endian bit
|
|
|
|
* fields. This can be achieved by XOR with 0x38 (64b) or 0x18 (32b).
|
|
|
|
*
|
|
|
|
* We also have special functions which work with an MSB0 encoding:
|
|
|
|
* on an s390x system the bits are numbered:
|
|
|
|
* |0..............63|64............127|128...........191|192...........255|
|
|
|
|
* and on s390:
|
|
|
|
* |0.....31|31....63|64....95|96...127|128..159|160..191|192..223|224..255|
|
|
|
|
*
|
|
|
|
* The main difference is that bit 0-63 (64b) or 0-31 (32b) in the bit
|
|
|
|
* number field needs to be reversed compared to the LSB0 encoded bit
|
|
|
|
* fields. This can be achieved by XOR with 0x3f (64b) or 0x1f (32b).
|
2005-04-17 00:20:36 +02:00
|
|
|
*
|
|
|
|
*/
|
2006-06-29 14:56:13 +02:00
|
|
|
|
2012-07-20 11:15:04 +02:00
|
|
|
#ifndef _S390_BITOPS_H
|
|
|
|
#define _S390_BITOPS_H
|
|
|
|
|
2007-10-19 08:40:26 +02:00
|
|
|
#ifndef _LINUX_BITOPS_H
|
|
|
|
#error only <linux/bitops.h> can be included directly
|
|
|
|
#endif
|
|
|
|
|
2013-09-16 16:22:05 +02:00
|
|
|
#include <linux/typecheck.h>
|
2005-04-17 00:20:36 +02:00
|
|
|
#include <linux/compiler.h>
|
|
|
|
|
2012-05-23 16:24:51 +02:00
|
|
|
#ifndef CONFIG_64BIT
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
#define __BITOPS_OR "or"
|
|
|
|
#define __BITOPS_AND "nr"
|
|
|
|
#define __BITOPS_XOR "xr"
|
|
|
|
|
2013-09-10 15:35:39 +02:00
|
|
|
#define __BITOPS_LOOP(__addr, __val, __op_string) \
|
|
|
|
({ \
|
|
|
|
unsigned long __old, __new; \
|
|
|
|
\
|
2013-09-16 16:22:05 +02:00
|
|
|
typecheck(unsigned long *, (__addr)); \
|
2006-09-28 16:56:43 +02:00
|
|
|
asm volatile( \
|
|
|
|
" l %0,%2\n" \
|
|
|
|
"0: lr %1,%0\n" \
|
|
|
|
__op_string " %1,%3\n" \
|
|
|
|
" cs %0,%1,%2\n" \
|
|
|
|
" jl 0b" \
|
2013-09-16 16:22:05 +02:00
|
|
|
: "=&d" (__old), "=&d" (__new), "+Q" (*(__addr))\
|
|
|
|
: "d" (__val) \
|
2013-09-10 15:35:39 +02:00
|
|
|
: "cc"); \
|
|
|
|
__old; \
|
|
|
|
})
|
2006-09-28 16:56:43 +02:00
|
|
|
|
2012-05-23 16:24:51 +02:00
|
|
|
#else /* CONFIG_64BIT */
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2013-09-10 15:35:39 +02:00
|
|
|
#ifdef CONFIG_HAVE_MARCH_Z196_FEATURES
|
|
|
|
|
|
|
|
#define __BITOPS_OR "laog"
|
|
|
|
#define __BITOPS_AND "lang"
|
|
|
|
#define __BITOPS_XOR "laxg"
|
|
|
|
|
|
|
|
#define __BITOPS_LOOP(__addr, __val, __op_string) \
|
|
|
|
({ \
|
|
|
|
unsigned long __old; \
|
|
|
|
\
|
2013-09-16 16:22:05 +02:00
|
|
|
typecheck(unsigned long *, (__addr)); \
|
2013-09-10 15:35:39 +02:00
|
|
|
asm volatile( \
|
|
|
|
__op_string " %0,%2,%1\n" \
|
2013-09-16 16:22:05 +02:00
|
|
|
: "=d" (__old), "+Q" (*(__addr)) \
|
2013-09-10 15:35:39 +02:00
|
|
|
: "d" (__val) \
|
|
|
|
: "cc"); \
|
|
|
|
__old; \
|
|
|
|
})
|
|
|
|
|
|
|
|
#else /* CONFIG_HAVE_MARCH_Z196_FEATURES */
|
|
|
|
|
2005-04-17 00:20:36 +02:00
|
|
|
#define __BITOPS_OR "ogr"
|
|
|
|
#define __BITOPS_AND "ngr"
|
|
|
|
#define __BITOPS_XOR "xgr"
|
|
|
|
|
2013-09-10 15:35:39 +02:00
|
|
|
#define __BITOPS_LOOP(__addr, __val, __op_string) \
|
|
|
|
({ \
|
|
|
|
unsigned long __old, __new; \
|
|
|
|
\
|
2013-09-16 16:22:05 +02:00
|
|
|
typecheck(unsigned long *, (__addr)); \
|
2006-09-28 16:56:43 +02:00
|
|
|
asm volatile( \
|
|
|
|
" lg %0,%2\n" \
|
|
|
|
"0: lgr %1,%0\n" \
|
|
|
|
__op_string " %1,%3\n" \
|
|
|
|
" csg %0,%1,%2\n" \
|
|
|
|
" jl 0b" \
|
2013-09-16 16:22:05 +02:00
|
|
|
: "=&d" (__old), "=&d" (__new), "+Q" (*(__addr))\
|
|
|
|
: "d" (__val) \
|
2013-09-10 15:35:39 +02:00
|
|
|
: "cc"); \
|
|
|
|
__old; \
|
|
|
|
})
|
|
|
|
|
|
|
|
#endif /* CONFIG_HAVE_MARCH_Z196_FEATURES */
|
2006-09-28 16:56:43 +02:00
|
|
|
|
2012-05-23 16:24:51 +02:00
|
|
|
#endif /* CONFIG_64BIT */
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2013-03-23 15:05:29 +01:00
|
|
|
#define __BITOPS_WORDS(bits) (((bits) + BITS_PER_LONG - 1) / BITS_PER_LONG)
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2013-09-16 16:22:05 +02:00
|
|
|
static inline unsigned long *
|
|
|
|
__bitops_word(unsigned long nr, volatile unsigned long *ptr)
|
|
|
|
{
|
|
|
|
unsigned long addr;
|
|
|
|
|
|
|
|
addr = (unsigned long)ptr + ((nr ^ (nr & (BITS_PER_LONG - 1))) >> 3);
|
|
|
|
return (unsigned long *)addr;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline unsigned char *
|
|
|
|
__bitops_byte(unsigned long nr, volatile unsigned long *ptr)
|
|
|
|
{
|
|
|
|
return ((unsigned char *)ptr) + ((nr ^ (BITS_PER_LONG - 8)) >> 3);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void set_bit(unsigned long nr, volatile unsigned long *ptr)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2013-09-16 16:22:05 +02:00
|
|
|
unsigned long *addr = __bitops_word(nr, ptr);
|
|
|
|
unsigned long mask;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2013-09-17 09:48:44 +02:00
|
|
|
#ifdef CONFIG_HAVE_MARCH_ZEC12_FEATURES
|
|
|
|
if (__builtin_constant_p(nr)) {
|
|
|
|
unsigned char *caddr = __bitops_byte(nr, ptr);
|
|
|
|
|
|
|
|
asm volatile(
|
|
|
|
"oi %0,%b1\n"
|
|
|
|
: "+Q" (*caddr)
|
|
|
|
: "i" (1 << (nr & 7))
|
|
|
|
: "cc");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif
|
2013-03-23 15:05:29 +01:00
|
|
|
mask = 1UL << (nr & (BITS_PER_LONG - 1));
|
2013-09-10 15:35:39 +02:00
|
|
|
__BITOPS_LOOP(addr, mask, __BITOPS_OR);
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
|
2013-09-16 16:22:05 +02:00
|
|
|
static inline void clear_bit(unsigned long nr, volatile unsigned long *ptr)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2013-09-16 16:22:05 +02:00
|
|
|
unsigned long *addr = __bitops_word(nr, ptr);
|
|
|
|
unsigned long mask;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2013-09-17 09:48:44 +02:00
|
|
|
#ifdef CONFIG_HAVE_MARCH_ZEC12_FEATURES
|
|
|
|
if (__builtin_constant_p(nr)) {
|
|
|
|
unsigned char *caddr = __bitops_byte(nr, ptr);
|
|
|
|
|
|
|
|
asm volatile(
|
|
|
|
"ni %0,%b1\n"
|
|
|
|
: "+Q" (*caddr)
|
|
|
|
: "i" (~(1 << (nr & 7)))
|
|
|
|
: "cc");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif
|
2013-03-23 15:05:29 +01:00
|
|
|
mask = ~(1UL << (nr & (BITS_PER_LONG - 1)));
|
2013-09-10 15:35:39 +02:00
|
|
|
__BITOPS_LOOP(addr, mask, __BITOPS_AND);
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
|
2013-09-16 16:22:05 +02:00
|
|
|
static inline void change_bit(unsigned long nr, volatile unsigned long *ptr)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2013-09-16 16:22:05 +02:00
|
|
|
unsigned long *addr = __bitops_word(nr, ptr);
|
|
|
|
unsigned long mask;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2013-09-17 09:48:44 +02:00
|
|
|
#ifdef CONFIG_HAVE_MARCH_ZEC12_FEATURES
|
|
|
|
if (__builtin_constant_p(nr)) {
|
|
|
|
unsigned char *caddr = __bitops_byte(nr, ptr);
|
|
|
|
|
|
|
|
asm volatile(
|
|
|
|
"xi %0,%b1\n"
|
|
|
|
: "+Q" (*caddr)
|
|
|
|
: "i" (1 << (nr & 7))
|
|
|
|
: "cc");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif
|
2013-03-23 15:05:29 +01:00
|
|
|
mask = 1UL << (nr & (BITS_PER_LONG - 1));
|
2013-09-10 15:35:39 +02:00
|
|
|
__BITOPS_LOOP(addr, mask, __BITOPS_XOR);
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline int
|
2013-09-16 16:22:05 +02:00
|
|
|
test_and_set_bit(unsigned long nr, volatile unsigned long *ptr)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2013-09-16 16:22:05 +02:00
|
|
|
unsigned long *addr = __bitops_word(nr, ptr);
|
|
|
|
unsigned long old, mask;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2013-03-23 15:05:29 +01:00
|
|
|
mask = 1UL << (nr & (BITS_PER_LONG - 1));
|
2013-09-10 15:35:39 +02:00
|
|
|
old = __BITOPS_LOOP(addr, mask, __BITOPS_OR);
|
2013-03-26 09:05:36 +01:00
|
|
|
barrier();
|
2005-04-17 00:20:36 +02:00
|
|
|
return (old & mask) != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int
|
2013-09-16 16:22:05 +02:00
|
|
|
test_and_clear_bit(unsigned long nr, volatile unsigned long *ptr)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2013-09-16 16:22:05 +02:00
|
|
|
unsigned long *addr = __bitops_word(nr, ptr);
|
|
|
|
unsigned long old, mask;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2013-03-23 15:05:29 +01:00
|
|
|
mask = ~(1UL << (nr & (BITS_PER_LONG - 1)));
|
2013-09-10 15:35:39 +02:00
|
|
|
old = __BITOPS_LOOP(addr, mask, __BITOPS_AND);
|
2013-03-26 09:05:36 +01:00
|
|
|
barrier();
|
2013-09-10 15:35:39 +02:00
|
|
|
return (old & ~mask) != 0;
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline int
|
2013-09-16 16:22:05 +02:00
|
|
|
test_and_change_bit(unsigned long nr, volatile unsigned long *ptr)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2013-09-16 16:22:05 +02:00
|
|
|
unsigned long *addr = __bitops_word(nr, ptr);
|
|
|
|
unsigned long old, mask;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2013-03-23 15:05:29 +01:00
|
|
|
mask = 1UL << (nr & (BITS_PER_LONG - 1));
|
2013-09-10 15:35:39 +02:00
|
|
|
old = __BITOPS_LOOP(addr, mask, __BITOPS_XOR);
|
2013-03-26 09:05:36 +01:00
|
|
|
barrier();
|
2005-04-17 00:20:36 +02:00
|
|
|
return (old & mask) != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void __set_bit(unsigned long nr, volatile unsigned long *ptr)
|
|
|
|
{
|
2013-09-16 16:22:05 +02:00
|
|
|
unsigned char *addr = __bitops_byte(nr, ptr);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2013-09-16 16:22:05 +02:00
|
|
|
*addr |= 1 << (nr & 7);
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
__clear_bit(unsigned long nr, volatile unsigned long *ptr)
|
|
|
|
{
|
2013-09-16 16:22:05 +02:00
|
|
|
unsigned char *addr = __bitops_byte(nr, ptr);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2013-09-16 16:22:05 +02:00
|
|
|
*addr &= ~(1 << (nr & 7));
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline void __change_bit(unsigned long nr, volatile unsigned long *ptr)
|
|
|
|
{
|
2013-09-16 16:22:05 +02:00
|
|
|
unsigned char *addr = __bitops_byte(nr, ptr);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2013-09-16 16:22:05 +02:00
|
|
|
*addr ^= 1 << (nr & 7);
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline int
|
2013-09-16 16:22:05 +02:00
|
|
|
__test_and_set_bit(unsigned long nr, volatile unsigned long *ptr)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2013-09-16 16:22:05 +02:00
|
|
|
unsigned char *addr = __bitops_byte(nr, ptr);
|
2005-04-17 00:20:36 +02:00
|
|
|
unsigned char ch;
|
|
|
|
|
2013-09-16 16:22:05 +02:00
|
|
|
ch = *addr;
|
|
|
|
*addr |= 1 << (nr & 7);
|
2005-04-17 00:20:36 +02:00
|
|
|
return (ch >> (nr & 7)) & 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int
|
2013-09-16 16:22:05 +02:00
|
|
|
__test_and_clear_bit(unsigned long nr, volatile unsigned long *ptr)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2013-09-16 16:22:05 +02:00
|
|
|
unsigned char *addr = __bitops_byte(nr, ptr);
|
2005-04-17 00:20:36 +02:00
|
|
|
unsigned char ch;
|
|
|
|
|
2013-09-16 16:22:05 +02:00
|
|
|
ch = *addr;
|
|
|
|
*addr &= ~(1 << (nr & 7));
|
2005-04-17 00:20:36 +02:00
|
|
|
return (ch >> (nr & 7)) & 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int
|
2013-09-16 16:22:05 +02:00
|
|
|
__test_and_change_bit(unsigned long nr, volatile unsigned long *ptr)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2013-09-16 16:22:05 +02:00
|
|
|
unsigned char *addr = __bitops_byte(nr, ptr);
|
2005-04-17 00:20:36 +02:00
|
|
|
unsigned char ch;
|
|
|
|
|
2013-09-16 16:22:05 +02:00
|
|
|
ch = *addr;
|
|
|
|
*addr ^= 1 << (nr & 7);
|
2005-04-17 00:20:36 +02:00
|
|
|
return (ch >> (nr & 7)) & 1;
|
|
|
|
}
|
|
|
|
|
2013-09-16 16:22:05 +02:00
|
|
|
static inline int test_bit(unsigned long nr, const volatile unsigned long *ptr)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2013-09-16 16:22:05 +02:00
|
|
|
const volatile unsigned char *addr;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2013-09-16 16:22:05 +02:00
|
|
|
addr = ((const volatile unsigned char *)ptr);
|
|
|
|
addr += (nr ^ (BITS_PER_LONG - 8)) >> 3;
|
|
|
|
return (*addr >> (nr & 7)) & 1;
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
|
2005-07-27 20:44:58 +02:00
|
|
|
/*
|
2013-09-18 11:45:36 +02:00
|
|
|
* ATTENTION:
|
|
|
|
* find_first_bit_left() and find_next_bit_left() use MSB0 encoding.
|
2012-11-29 13:01:15 +01:00
|
|
|
*/
|
2013-09-18 11:45:36 +02:00
|
|
|
unsigned long find_first_bit_left(const unsigned long *addr, unsigned long size);
|
|
|
|
unsigned long find_next_bit_left(const unsigned long *addr, unsigned long size,
|
|
|
|
unsigned long offset);
|
2012-11-29 13:01:15 +01:00
|
|
|
|
2013-09-18 11:45:36 +02:00
|
|
|
#include <asm-generic/bitops/__ffs.h>
|
|
|
|
#include <asm-generic/bitops/ffs.h>
|
2008-03-15 18:31:49 +01:00
|
|
|
#include <asm-generic/bitops/__fls.h>
|
2013-09-18 11:45:36 +02:00
|
|
|
#include <asm-generic/bitops/fls.h>
|
2006-03-26 11:39:34 +02:00
|
|
|
#include <asm-generic/bitops/fls64.h>
|
2013-09-18 11:45:36 +02:00
|
|
|
#include <asm-generic/bitops/ffz.h>
|
|
|
|
#include <asm-generic/bitops/find.h>
|
2006-03-26 11:39:34 +02:00
|
|
|
#include <asm-generic/bitops/hweight.h>
|
2007-10-18 12:06:39 +02:00
|
|
|
#include <asm-generic/bitops/lock.h>
|
2013-09-18 11:45:36 +02:00
|
|
|
#include <asm-generic/bitops/sched.h>
|
2011-05-27 01:26:12 +02:00
|
|
|
#include <asm-generic/bitops/le.h>
|
2011-07-27 01:09:04 +02:00
|
|
|
#include <asm-generic/bitops/ext2-atomic-setbit.h>
|
2008-02-05 16:50:44 +01:00
|
|
|
|
2005-04-17 00:20:36 +02:00
|
|
|
#endif /* _S390_BITOPS_H */
|