1/* 2 * Atomic operations that C can't guarantee us. Useful for 3 * resource counting etc. 4 * 5 * But use these as seldom as possible since they are slower than 6 * regular operations. 7 * 8 * Copyright (C) 2004-2006 Atmel Corporation 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License version 2 as 12 * published by the Free Software Foundation. 13 */ 14#ifndef __ASM_AVR32_ATOMIC_H 15#define __ASM_AVR32_ATOMIC_H 16 17#include <linux/types.h> 18#include <asm/cmpxchg.h> 19 20#define ATOMIC_INIT(i) { (i) } 21 22#define atomic_read(v) ACCESS_ONCE((v)->counter) 23#define atomic_set(v, i) (((v)->counter) = i) 24 25#define ATOMIC_OP_RETURN(op, asm_op, asm_con) \ 26static inline int __atomic_##op##_return(int i, atomic_t *v) \ 27{ \ 28 int result; \ 29 \ 30 asm volatile( \ 31 "/* atomic_" #op "_return */\n" \ 32 "1: ssrf 5\n" \ 33 " ld.w %0, %2\n" \ 34 " " #asm_op " %0, %3\n" \ 35 " stcond %1, %0\n" \ 36 " brne 1b" \ 37 : "=&r" (result), "=o" (v->counter) \ 38 : "m" (v->counter), #asm_con (i) \ 39 : "cc"); \ 40 \ 41 return result; \ 42} 43 44ATOMIC_OP_RETURN(sub, sub, rKs21) 45ATOMIC_OP_RETURN(add, add, r) 46 47#undef ATOMIC_OP_RETURN 48 49/* 50 * Probably found the reason why we want to use sub with the signed 21-bit 51 * limit, it uses one less register than the add instruction that can add up to 52 * 32-bit values. 53 * 54 * Both instructions are 32-bit, to use a 16-bit instruction the immediate is 55 * very small; 4 bit. 56 * 57 * sub 32-bit, type IV, takes a register and subtracts a 21-bit immediate. 58 * add 32-bit, type II, adds two register values together. 59 */ 60#define IS_21BIT_CONST(i) \ 61 (__builtin_constant_p(i) && ((i) >= -1048575) && ((i) <= 1048576)) 62 63/* 64 * atomic_add_return - add integer to atomic variable 65 * @i: integer value to add 66 * @v: pointer of type atomic_t 67 * 68 * Atomically adds @i to @v. Returns the resulting value. 69 */ 70static inline int atomic_add_return(int i, atomic_t *v) 71{ 72 if (IS_21BIT_CONST(i)) 73 return __atomic_sub_return(-i, v); 74 75 return __atomic_add_return(i, v); 76} 77 78/* 79 * atomic_sub_return - subtract the atomic variable 80 * @i: integer value to subtract 81 * @v: pointer of type atomic_t 82 * 83 * Atomically subtracts @i from @v. Returns the resulting value. 84 */ 85static inline int atomic_sub_return(int i, atomic_t *v) 86{ 87 if (IS_21BIT_CONST(i)) 88 return __atomic_sub_return(i, v); 89 90 return __atomic_add_return(-i, v); 91} 92 93/* 94 * __atomic_add_unless - add unless the number is a given value 95 * @v: pointer of type atomic_t 96 * @a: the amount to add to v... 97 * @u: ...unless v is equal to u. 98 * 99 * Atomically adds @a to @v, so long as it was not @u. 100 * Returns the old value of @v. 101*/ 102static inline int __atomic_add_unless(atomic_t *v, int a, int u) 103{ 104 int tmp, old = atomic_read(v); 105 106 if (IS_21BIT_CONST(a)) { 107 asm volatile( 108 "/* __atomic_sub_unless */\n" 109 "1: ssrf 5\n" 110 " ld.w %0, %2\n" 111 " cp.w %0, %4\n" 112 " breq 1f\n" 113 " sub %0, %3\n" 114 " stcond %1, %0\n" 115 " brne 1b\n" 116 "1:" 117 : "=&r"(tmp), "=o"(v->counter) 118 : "m"(v->counter), "rKs21"(-a), "rKs21"(u) 119 : "cc", "memory"); 120 } else { 121 asm volatile( 122 "/* __atomic_add_unless */\n" 123 "1: ssrf 5\n" 124 " ld.w %0, %2\n" 125 " cp.w %0, %4\n" 126 " breq 1f\n" 127 " add %0, %3\n" 128 " stcond %1, %0\n" 129 " brne 1b\n" 130 "1:" 131 : "=&r"(tmp), "=o"(v->counter) 132 : "m"(v->counter), "r"(a), "ir"(u) 133 : "cc", "memory"); 134 } 135 136 return old; 137} 138 139#undef IS_21BIT_CONST 140 141/* 142 * atomic_sub_if_positive - conditionally subtract integer from atomic variable 143 * @i: integer value to subtract 144 * @v: pointer of type atomic_t 145 * 146 * Atomically test @v and subtract @i if @v is greater or equal than @i. 147 * The function returns the old value of @v minus @i. 148 */ 149static inline int atomic_sub_if_positive(int i, atomic_t *v) 150{ 151 int result; 152 153 asm volatile( 154 "/* atomic_sub_if_positive */\n" 155 "1: ssrf 5\n" 156 " ld.w %0, %2\n" 157 " sub %0, %3\n" 158 " brlt 1f\n" 159 " stcond %1, %0\n" 160 " brne 1b\n" 161 "1:" 162 : "=&r"(result), "=o"(v->counter) 163 : "m"(v->counter), "ir"(i) 164 : "cc", "memory"); 165 166 return result; 167} 168 169#define atomic_xchg(v, new) (xchg(&((v)->counter), new)) 170#define atomic_cmpxchg(v, o, n) (cmpxchg(&((v)->counter), (o), (n))) 171 172#define atomic_sub(i, v) (void)atomic_sub_return(i, v) 173#define atomic_add(i, v) (void)atomic_add_return(i, v) 174#define atomic_dec(v) atomic_sub(1, (v)) 175#define atomic_inc(v) atomic_add(1, (v)) 176 177#define atomic_dec_return(v) atomic_sub_return(1, v) 178#define atomic_inc_return(v) atomic_add_return(1, v) 179 180#define atomic_sub_and_test(i, v) (atomic_sub_return(i, v) == 0) 181#define atomic_inc_and_test(v) (atomic_add_return(1, v) == 0) 182#define atomic_dec_and_test(v) (atomic_sub_return(1, v) == 0) 183#define atomic_add_negative(i, v) (atomic_add_return(i, v) < 0) 184 185#define atomic_dec_if_positive(v) atomic_sub_if_positive(1, v) 186 187#endif /* __ASM_AVR32_ATOMIC_H */ 188