bitops.h 9.82 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/*
 * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#ifndef _ASM_BITOPS_H
#define _ASM_BITOPS_H

#ifndef _LINUX_BITOPS_H
#error only <linux/bitops.h> can be included directly
#endif

#ifndef __ASSEMBLY__

#include <linux/types.h>
#include <linux/compiler.h>
20
#include <asm/barrier.h>
21 22 23
#ifndef CONFIG_ARC_HAS_LLSC
#include <asm/smp.h>
#endif
24

25
#ifdef CONFIG_ARC_HAS_LLSC
26

27 28 29
/*
 * Hardware assisted Atomic-R-M-W
 */
30

31 32 33 34 35 36 37
#define BIT_OP(op, c_op, asm_op)					\
static inline void op##_bit(unsigned long nr, volatile unsigned long *m)\
{									\
	unsigned int temp;						\
									\
	m += nr >> 5;							\
									\
38
	nr &= 0x1f;							\
39 40 41 42 43 44 45 46 47 48
									\
	__asm__ __volatile__(						\
	"1:	llock       %0, [%1]		\n"			\
	"	" #asm_op " %0, %0, %2	\n"				\
	"	scond       %0, [%1]		\n"			\
	"	bnz         1b			\n"			\
	: "=&r"(temp)	/* Early clobber, to prevent reg reuse */	\
	: "r"(m),	/* Not "m": llock only supports reg direct addr mode */	\
	  "ir"(nr)							\
	: "cc");							\
49 50 51 52 53 54 55 56 57 58 59 60 61
}

/*
 * Semantically:
 *    Test the bit
 *    if clear
 *        set it and return 0 (old value)
 *    else
 *        return 1 (old value).
 *
 * Since ARC lacks a equivalent h/w primitive, the bit is set unconditionally
 * and the old value of bit is returned
 */
62 63 64 65 66 67 68
#define TEST_N_BIT_OP(op, c_op, asm_op)					\
static inline int test_and_##op##_bit(unsigned long nr, volatile unsigned long *m)\
{									\
	unsigned long old, temp;					\
									\
	m += nr >> 5;							\
									\
69
	nr &= 0x1f;							\
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
									\
	/*								\
	 * Explicit full memory barrier needed before/after as		\
	 * LLOCK/SCOND themselves don't provide any such smenatic	\
	 */								\
	smp_mb();							\
									\
	__asm__ __volatile__(						\
	"1:	llock       %0, [%2]	\n"				\
	"	" #asm_op " %1, %0, %3	\n"				\
	"	scond       %1, [%2]	\n"				\
	"	bnz         1b		\n"				\
	: "=&r"(old), "=&r"(temp)					\
	: "r"(m), "ir"(nr)						\
	: "cc");							\
									\
	smp_mb();							\
									\
	return (old & (1 << nr)) != 0;					\
89 90
}

91
#elif !defined(CONFIG_ARC_PLAT_EZNPS)
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108

/*
 * Non hardware assisted Atomic-R-M-W
 * Locking would change to irq-disabling only (UP) and spinlocks (SMP)
 *
 * There's "significant" micro-optimization in writing our own variants of
 * bitops (over generic variants)
 *
 * (1) The generic APIs have "signed" @nr while we have it "unsigned"
 *     This avoids extra code to be generated for pointer arithmatic, since
 *     is "not sure" that index is NOT -ve
 * (2) Utilize the fact that ARCompact bit fidding insn (BSET/BCLR/ASL) etc
 *     only consider bottom 5 bits of @nr, so NO need to mask them off.
 *     (GCC Quirk: however for constant @nr we still need to do the masking
 *             at compile time)
 */

109 110 111 112 113 114 115 116 117 118 119 120
#define BIT_OP(op, c_op, asm_op)					\
static inline void op##_bit(unsigned long nr, volatile unsigned long *m)\
{									\
	unsigned long temp, flags;					\
	m += nr >> 5;							\
									\
	/*								\
	 * spin lock/unlock provide the needed smp_mb() before/after	\
	 */								\
	bitops_lock(flags);						\
									\
	temp = *m;							\
121
	*m = temp c_op (1UL << (nr & 0x1f));					\
122 123 124 125 126 127 128 129 130 131 132 133 134
									\
	bitops_unlock(flags);						\
}

#define TEST_N_BIT_OP(op, c_op, asm_op)					\
static inline int test_and_##op##_bit(unsigned long nr, volatile unsigned long *m)\
{									\
	unsigned long old, flags;					\
	m += nr >> 5;							\
									\
	bitops_lock(flags);						\
									\
	old = *m;							\
135
	*m = old c_op (1UL << (nr & 0x1f));				\
136 137 138
									\
	bitops_unlock(flags);						\
									\
139
	return (old & (1UL << (nr & 0x1f))) != 0;			\
140 141
}

142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
#else /* CONFIG_ARC_PLAT_EZNPS */

#define BIT_OP(op, c_op, asm_op)					\
static inline void op##_bit(unsigned long nr, volatile unsigned long *m)\
{									\
	m += nr >> 5;							\
									\
	nr = (1UL << (nr & 0x1f));					\
	if (asm_op == CTOP_INST_AAND_DI_R2_R2_R3)			\
		nr = ~nr;						\
									\
	__asm__ __volatile__(						\
	"	mov r2, %0\n"						\
	"	mov r3, %1\n"						\
	"	.word %2\n"						\
	:								\
	: "r"(nr), "r"(m), "i"(asm_op)					\
	: "r2", "r3", "memory");					\
}

#define TEST_N_BIT_OP(op, c_op, asm_op)					\
static inline int test_and_##op##_bit(unsigned long nr, volatile unsigned long *m)\
{									\
	unsigned long old;						\
									\
	m += nr >> 5;							\
									\
	nr = old = (1UL << (nr & 0x1f));				\
	if (asm_op == CTOP_INST_AAND_DI_R2_R2_R3)			\
		old = ~old;						\
									\
	/* Explicit full memory barrier needed before/after */		\
	smp_mb();							\
									\
	__asm__ __volatile__(						\
	"	mov r2, %0\n"						\
	"	mov r3, %1\n"						\
	"       .word %2\n"						\
	"	mov %0, r2"						\
	: "+r"(old)							\
	: "r"(m), "i"(asm_op)						\
	: "r2", "r3", "memory");					\
									\
	smp_mb();							\
									\
	return (old & nr) != 0;					\
}

#endif /* CONFIG_ARC_PLAT_EZNPS */
191 192 193 194 195

/***************************************
 * Non atomic variants
 **************************************/

196 197 198 199 200 201 202
#define __BIT_OP(op, c_op, asm_op)					\
static inline void __##op##_bit(unsigned long nr, volatile unsigned long *m)	\
{									\
	unsigned long temp;						\
	m += nr >> 5;							\
									\
	temp = *m;							\
203
	*m = temp c_op (1UL << (nr & 0x1f));				\
204 205 206 207 208 209 210 211 212
}

#define __TEST_N_BIT_OP(op, c_op, asm_op)				\
static inline int __test_and_##op##_bit(unsigned long nr, volatile unsigned long *m)\
{									\
	unsigned long old;						\
	m += nr >> 5;							\
									\
	old = *m;							\
213
	*m = old c_op (1UL << (nr & 0x1f));				\
214
									\
215
	return (old & (1UL << (nr & 0x1f))) != 0;			\
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
}

#define BIT_OPS(op, c_op, asm_op)					\
									\
	/* set_bit(), clear_bit(), change_bit() */			\
	BIT_OP(op, c_op, asm_op)					\
									\
	/* test_and_set_bit(), test_and_clear_bit(), test_and_change_bit() */\
	TEST_N_BIT_OP(op, c_op, asm_op)					\
									\
	/* __set_bit(), __clear_bit(), __change_bit() */		\
	__BIT_OP(op, c_op, asm_op)					\
									\
	/* __test_and_set_bit(), __test_and_clear_bit(), __test_and_change_bit() */\
	__TEST_N_BIT_OP(op, c_op, asm_op)

232
#ifndef CONFIG_ARC_PLAT_EZNPS
233 234 235
BIT_OPS(set, |, bset)
BIT_OPS(clear, & ~, bclr)
BIT_OPS(change, ^, bxor)
236 237 238 239 240
#else
BIT_OPS(set, |, CTOP_INST_AOR_DI_R2_R2_R3)
BIT_OPS(clear, & ~, CTOP_INST_AAND_DI_R2_R2_R3)
BIT_OPS(change, ^, CTOP_INST_AXOR_DI_R2_R2_R3)
#endif
241 242 243 244 245

/*
 * This routine doesn't need to be atomic.
 */
static inline int
246
test_bit(unsigned int nr, const volatile unsigned long *addr)
247 248 249 250 251
{
	unsigned long mask;

	addr += nr >> 5;

252
	mask = 1UL << (nr & 0x1f);
253 254 255 256

	return ((mask & *addr) != 0);
}

257 258
#ifdef CONFIG_ISA_ARCOMPACT

259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
/*
 * Count the number of zeros, starting from MSB
 * Helper for fls( ) friends
 * This is a pure count, so (1-32) or (0-31) doesn't apply
 * It could be 0 to 32, based on num of 0's in there
 * clz(0x8000_0000) = 0, clz(0xFFFF_FFFF)=0, clz(0) = 32, clz(1) = 31
 */
static inline __attribute__ ((const)) int clz(unsigned int x)
{
	unsigned int res;

	__asm__ __volatile__(
	"	norm.f  %0, %1		\n"
	"	mov.n   %0, 0		\n"
	"	add.p   %0, %0, 1	\n"
	: "=r"(res)
	: "r"(x)
	: "cc");

	return res;
}

281
static inline int constant_fls(unsigned int x)
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314
{
	int r = 32;

	if (!x)
		return 0;
	if (!(x & 0xffff0000u)) {
		x <<= 16;
		r -= 16;
	}
	if (!(x & 0xff000000u)) {
		x <<= 8;
		r -= 8;
	}
	if (!(x & 0xf0000000u)) {
		x <<= 4;
		r -= 4;
	}
	if (!(x & 0xc0000000u)) {
		x <<= 2;
		r -= 2;
	}
	if (!(x & 0x80000000u)) {
		x <<= 1;
		r -= 1;
	}
	return r;
}

/*
 * fls = Find Last Set in word
 * @result: [1-32]
 * fls(1) = 1, fls(0x80000000) = 32, fls(0) = 0
 */
315
static inline __attribute__ ((const)) int fls(unsigned int x)
316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342
{
	if (__builtin_constant_p(x))
	       return constant_fls(x);

	return 32 - clz(x);
}

/*
 * __fls: Similar to fls, but zero based (0-31)
 */
static inline __attribute__ ((const)) int __fls(unsigned long x)
{
	if (!x)
		return 0;
	else
		return fls(x) - 1;
}

/*
 * ffs = Find First Set in word (LSB to MSB)
 * @result: [1-32], 0 if all 0's
 */
#define ffs(x)	({ unsigned long __t = (x); fls(__t & -__t); })

/*
 * __ffs: Similar to ffs, but zero based (0-31)
 */
343
static inline __attribute__ ((const)) unsigned long __ffs(unsigned long word)
344 345 346 347 348 349 350
{
	if (!word)
		return word;

	return ffs(word) - 1;
}

351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402
#else	/* CONFIG_ISA_ARCV2 */

/*
 * fls = Find Last Set in word
 * @result: [1-32]
 * fls(1) = 1, fls(0x80000000) = 32, fls(0) = 0
 */
static inline __attribute__ ((const)) int fls(unsigned long x)
{
	int n;

	asm volatile(
	"	fls.f	%0, %1		\n"  /* 0:31; 0(Z) if src 0 */
	"	add.nz	%0, %0, 1	\n"  /* 0:31 -> 1:32 */
	: "=r"(n)	/* Early clobber not needed */
	: "r"(x)
	: "cc");

	return n;
}

/*
 * __fls: Similar to fls, but zero based (0-31). Also 0 if no bit set
 */
static inline __attribute__ ((const)) int __fls(unsigned long x)
{
	/* FLS insn has exactly same semantics as the API */
	return	__builtin_arc_fls(x);
}

/*
 * ffs = Find First Set in word (LSB to MSB)
 * @result: [1-32], 0 if all 0's
 */
static inline __attribute__ ((const)) int ffs(unsigned long x)
{
	int n;

	asm volatile(
	"	ffs.f	%0, %1		\n"  /* 0:31; 31(Z) if src 0 */
	"	add.nz	%0, %0, 1	\n"  /* 0:31 -> 1:32 */
	"	mov.z	%0, 0		\n"  /* 31(Z)-> 0 */
	: "=r"(n)	/* Early clobber not needed */
	: "r"(x)
	: "cc");

	return n;
}

/*
 * __ffs: Similar to ffs, but zero based (0-31)
 */
403
static inline __attribute__ ((const)) unsigned long __ffs(unsigned long x)
404
{
405
	unsigned long n;
406 407 408 409 410 411 412 413 414 415 416 417 418 419

	asm volatile(
	"	ffs.f	%0, %1		\n"  /* 0:31; 31(Z) if src 0 */
	"	mov.z	%0, 0		\n"  /* 31(Z)-> 0 */
	: "=r"(n)
	: "r"(x)
	: "cc");

	return n;

}

#endif	/* CONFIG_ISA_ARCOMPACT */

420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437
/*
 * ffz = Find First Zero in word.
 * @return:[0-31], 32 if all 1's
 */
#define ffz(x)	__ffs(~(x))

#include <asm-generic/bitops/hweight.h>
#include <asm-generic/bitops/fls64.h>
#include <asm-generic/bitops/sched.h>
#include <asm-generic/bitops/lock.h>

#include <asm-generic/bitops/find.h>
#include <asm-generic/bitops/le.h>
#include <asm-generic/bitops/ext2-atomic-setbit.h>

#endif /* !__ASSEMBLY__ */

#endif