1 /* SPDX-License-Identifier: GPL-2.0-only */
2 /* Copyright (C) 2012 Regents of the University of California */
3
4 #ifndef ASM__RISCV__BITOPS_H
5 #define ASM__RISCV__BITOPS_H
6
7 #include <asm/system.h>
8
9 #if BITOP_BITS_PER_WORD == 64
10 #define __AMO(op) "amo" #op ".d"
11 #elif BITOP_BITS_PER_WORD == 32
12 #define __AMO(op) "amo" #op ".w"
13 #else
14 #error "Unexpected BITOP_BITS_PER_WORD"
15 #endif
16
17 /* Based on linux/arch/include/asm/bitops.h */
18
19 /*
20 * Non-atomic bit manipulation.
21 *
22 * Implemented using atomics to be interrupt safe. Could alternatively
23 * implement with local interrupt masking.
24 */
25 #define __set_bit(n, p) set_bit(n, p)
26 #define __clear_bit(n, p) clear_bit(n, p)
27
28 #define test_and_op_bit_ord(op, mod, nr, addr, ord) \
29 ({ \
30 bitop_uint_t res, mask; \
31 mask = BITOP_MASK(nr); \
32 asm volatile ( \
33 __AMO(op) #ord " %0, %2, %1" \
34 : "=r" (res), "+A" (addr[BITOP_WORD(nr)]) \
35 : "r" (mod(mask)) \
36 : "memory"); \
37 ((res & mask) != 0); \
38 })
39
40 #define op_bit_ord(op, mod, nr, addr, ord) \
41 asm volatile ( \
42 __AMO(op) #ord " zero, %1, %0" \
43 : "+A" (addr[BITOP_WORD(nr)]) \
44 : "r" (mod(BITOP_MASK(nr))) \
45 : "memory");
46
47 #define test_and_op_bit(op, mod, nr, addr) \
48 test_and_op_bit_ord(op, mod, nr, addr, .aqrl)
49 #define op_bit(op, mod, nr, addr) \
50 op_bit_ord(op, mod, nr, addr, )
51
52 /* Bitmask modifiers */
53 #define NOP(x) (x)
54 #define NOT(x) (~(x))
55
56 /**
57 * test_and_set_bit - Set a bit and return its old value
58 * @nr: Bit to set
59 * @addr: Address to count from
60 */
test_and_set_bit(int nr,volatile void * p)61 static inline bool test_and_set_bit(int nr, volatile void *p)
62 {
63 volatile bitop_uint_t *addr = p;
64
65 return test_and_op_bit(or, NOP, nr, addr);
66 }
67
68 /**
69 * test_and_clear_bit - Clear a bit and return its old value
70 * @nr: Bit to clear
71 * @addr: Address to count from
72 */
test_and_clear_bit(int nr,volatile void * p)73 static inline bool test_and_clear_bit(int nr, volatile void *p)
74 {
75 volatile bitop_uint_t *addr = p;
76
77 return test_and_op_bit(and, NOT, nr, addr);
78 }
79
80 /**
81 * test_and_change_bit - Toggle (change) a bit and return its old value
82 * @nr: Bit to change
83 * @addr: Address to count from
84 *
85 * This operation is atomic and cannot be reordered.
86 * It also implies a memory barrier.
87 */
test_and_change_bit(int nr,volatile void * p)88 static inline bool test_and_change_bit(int nr, volatile void *p)
89 {
90 volatile bitop_uint_t *addr = p;
91
92 return test_and_op_bit(xor, NOP, nr, addr);
93 }
94
95 /**
96 * set_bit - Atomically set a bit in memory
97 * @nr: the bit to set
98 * @addr: the address to start counting from
99 *
100 * Note that @nr may be almost arbitrarily large; this function is not
101 * restricted to acting on a single-word quantity.
102 */
set_bit(int nr,volatile void * p)103 static inline void set_bit(int nr, volatile void *p)
104 {
105 volatile bitop_uint_t *addr = p;
106
107 op_bit(or, NOP, nr, addr);
108 }
109
110 /**
111 * clear_bit - Clears a bit in memory
112 * @nr: Bit to clear
113 * @addr: Address to start counting from
114 */
clear_bit(int nr,volatile void * p)115 static inline void clear_bit(int nr, volatile void *p)
116 {
117 volatile bitop_uint_t *addr = p;
118
119 op_bit(and, NOT, nr, addr);
120 }
121
122 #undef test_and_op_bit
123 #undef op_bit
124 #undef NOP
125 #undef NOT
126 #undef __AMO
127
128 #define arch_ffs(x) ((x) ? 1 + __builtin_ctz(x) : 0)
129 #define arch_ffsl(x) ((x) ? 1 + __builtin_ctzl(x) : 0)
130 #define arch_fls(x) ((x) ? BITS_PER_INT - __builtin_clz(x) : 0)
131 #define arch_flsl(x) ((x) ? BITS_PER_LONG - __builtin_clzl(x) : 0)
132
133 #define arch_hweightl(x) __builtin_popcountl(x)
134
135 #endif /* ASM__RISCV__BITOPS_H */
136
137 /*
138 * Local variables:
139 * mode: C
140 * c-file-style: "BSD"
141 * c-basic-offset: 4
142 * indent-tabs-mode: nil
143 * End:
144 */
145