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