1/* 2 * linux/include/asm-arm/atomic.h 3 * 4 * Copyright (C) 1996 Russell King. 5 * Copyright (C) 2002 Deep Blue Solutions Ltd. 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 2 as 9 * published by the Free Software Foundation. 10 */ 11#ifndef __ASM_ARM_ATOMIC_H 12#define __ASM_ARM_ATOMIC_H 13 14#include <linux/compiler.h> 15#include <asm/system.h> 16 17typedef struct { volatile int counter; } atomic_t; 18 19#define ATOMIC_INIT(i) { (i) } 20 21#ifdef __KERNEL__ 22 23#define atomic_read(v) ((v)->counter) 24 25#if __LINUX_ARM_ARCH__ >= 6 26 27/* 28 * ARMv6 UP and SMP safe atomic ops. We use load exclusive and 29 * store exclusive to ensure that these are atomic. We may loop 30 * to ensure that the update happens. Writing to 'v->counter' 31 * without using the following operations WILL break the atomic 32 * nature of these ops. 33 */ 34static inline void atomic_set(atomic_t *v, int i) 35{ 36 unsigned long tmp; 37 38 __asm__ __volatile__("@ atomic_set\n" 39"1: ldrex %0, [%1]\n" 40" strex %0, %2, [%1]\n" 41" teq %0, #0\n" 42" bne 1b" 43 : "=&r" (tmp) 44 : "r" (&v->counter), "r" (i) 45 : "cc"); 46} 47 48static inline int atomic_add_return(int i, atomic_t *v) 49{ 50 unsigned long tmp; 51 int result; 52 53 __asm__ __volatile__("@ atomic_add_return\n" 54"1: ldrex %0, [%2]\n" 55" add %0, %0, %3\n" 56" strex %1, %0, [%2]\n" 57" teq %1, #0\n" 58" bne 1b" 59 : "=&r" (result), "=&r" (tmp) 60 : "r" (&v->counter), "Ir" (i) 61 : "cc"); 62 63 return result; 64} 65 66static inline int atomic_sub_return(int i, atomic_t *v) 67{ 68 unsigned long tmp; 69 int result; 70 71 __asm__ __volatile__("@ atomic_sub_return\n" 72"1: ldrex %0, [%2]\n" 73" sub %0, %0, %3\n" 74" strex %1, %0, [%2]\n" 75" teq %1, #0\n" 76" bne 1b" 77 : "=&r" (result), "=&r" (tmp) 78 : "r" (&v->counter), "Ir" (i) 79 : "cc"); 80 81 return result; 82} 83 84static inline int atomic_cmpxchg(atomic_t *ptr, int old, int new) 85{ 86 unsigned long oldval, res; 87 88 do { 89 __asm__ __volatile__("@ atomic_cmpxchg\n" 90 "ldrex %1, [%2]\n" 91 "mov %0, #0\n" 92 "teq %1, %3\n" 93 "strexeq %0, %4, [%2]\n" 94 : "=&r" (res), "=&r" (oldval) 95 : "r" (&ptr->counter), "Ir" (old), "r" (new) 96 : "cc"); 97 } while (res); 98 99 return oldval; 100} 101 102static inline void atomic_clear_mask(unsigned long mask, unsigned long *addr) 103{ 104 unsigned long tmp, tmp2; 105 106 __asm__ __volatile__("@ atomic_clear_mask\n" 107"1: ldrex %0, [%2]\n" 108" bic %0, %0, %3\n" 109" strex %1, %0, [%2]\n" 110" teq %1, #0\n" 111" bne 1b" 112 : "=&r" (tmp), "=&r" (tmp2) 113 : "r" (addr), "Ir" (mask) 114 : "cc"); 115} 116 117#else /* ARM_ARCH_6 */ 118 119#include <asm/system.h> 120 121#ifdef CONFIG_SMP 122#error SMP not supported on pre-ARMv6 CPUs 123#endif 124 125#define atomic_set(v,i) (((v)->counter) = (i)) 126 127static inline int atomic_add_return(int i, atomic_t *v) 128{ 129 unsigned long flags; 130 int val; 131 132 raw_local_irq_save(flags); 133 val = v->counter; 134 v->counter = val += i; 135 raw_local_irq_restore(flags); 136 137 return val; 138} 139 140static inline int atomic_sub_return(int i, atomic_t *v) 141{ 142 unsigned long flags; 143 int val; 144 145 raw_local_irq_save(flags); 146 val = v->counter; 147 v->counter = val -= i; 148 raw_local_irq_restore(flags); 149 150 return val; 151} 152 153static inline int atomic_cmpxchg(atomic_t *v, int old, int new) 154{ 155 int ret; 156 unsigned long flags; 157 158 raw_local_irq_save(flags); 159 ret = v->counter; 160 if (likely(ret == old)) 161 v->counter = new; 162 raw_local_irq_restore(flags); 163 164 return ret; 165} 166 167static inline void atomic_clear_mask(unsigned long mask, unsigned long *addr) 168{ 169 unsigned long flags; 170 171 raw_local_irq_save(flags); 172 *addr &= ~mask; 173 raw_local_irq_restore(flags); 174} 175 176#endif /* __LINUX_ARM_ARCH__ */ 177 178#define atomic_xchg(v, new) (xchg(&((v)->counter), new)) 179 180static inline int atomic_add_unless(atomic_t *v, int a, int u) 181{ 182 int c, old; 183 184 c = atomic_read(v); 185 while (c != u && (old = atomic_cmpxchg((v), c, c + a)) != c) 186 c = old; 187 return c != u; 188} 189#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0) 190 191#define atomic_add(i, v) (void) atomic_add_return(i, v) 192#define atomic_inc(v) (void) atomic_add_return(1, v) 193#define atomic_sub(i, v) (void) atomic_sub_return(i, v) 194#define atomic_dec(v) (void) atomic_sub_return(1, v) 195 196#define atomic_inc_and_test(v) (atomic_add_return(1, v) == 0) 197#define atomic_dec_and_test(v) (atomic_sub_return(1, v) == 0) 198#define atomic_inc_return(v) (atomic_add_return(1, v)) 199#define atomic_dec_return(v) (atomic_sub_return(1, v)) 200#define atomic_sub_and_test(i, v) (atomic_sub_return(i, v) == 0) 201 202#define atomic_add_negative(i,v) (atomic_add_return(i, v) < 0) 203 204/* Atomic operations are already serializing on ARM */ 205#define smp_mb__before_atomic_dec() barrier() 206#define smp_mb__after_atomic_dec() barrier() 207#define smp_mb__before_atomic_inc() barrier() 208#define smp_mb__after_atomic_inc() barrier() 209 210#include <asm-generic/atomic.h> 211#endif 212#endif 213