summaryrefslogtreecommitdiffstats
path: root/arch/sparc64/lib/dec_and_lock.S
blob: 7e6fdaebedbab9bf088170e40420307f8f730ed7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
/* $Id: dec_and_lock.S,v 1.5 2001/11/18 00:12:56 davem Exp $
 * dec_and_lock.S: Sparc64 version of "atomic_dec_and_lock()"
 *                 using cas and ldstub instructions.
 *
 * Copyright (C) 2000 David S. Miller (davem@redhat.com)
 */
#include <linux/config.h>
#include <asm/thread_info.h>

	.text
	.align	64

	/* CAS basically works like this:
	 *
	 * void CAS(MEM, REG1, REG2)
	 * {
	 *   START_ATOMIC();
	 *   if (*(MEM) == REG1) {
	 *     TMP = *(MEM);
	 *     *(MEM) = REG2;
	 *     REG2 = TMP;
	 *   } else
	 *     REG2 = *(MEM);
	 *   END_ATOMIC();
	 * }
	 */

	.globl	_atomic_dec_and_lock
_atomic_dec_and_lock:	/* %o0 = counter, %o1 = lock */
loop1:	lduw	[%o0], %g2
	subcc	%g2, 1, %g7
	be,pn	%icc, start_to_zero
	 nop
nzero:	cas	[%o0], %g2, %g7
	cmp	%g2, %g7
	bne,pn	%icc, loop1
	 mov	0, %g1

out:
	membar	#StoreLoad | #StoreStore
	retl
	 mov	%g1, %o0
start_to_zero:
#ifdef CONFIG_PREEMPT
	ldsw	[%g6 + TI_PRE_COUNT], %g3
	add	%g3, 1, %g3
	stw	%g3, [%g6 + TI_PRE_COUNT]
#endif
to_zero:
	ldstub	[%o1], %g3
	brnz,pn	%g3, spin_on_lock
	 membar	#StoreLoad | #StoreStore
loop2:	cas	[%o0], %g2, %g7		/* ASSERT(g7 == 0) */
	cmp	%g2, %g7

	be,pt	%icc, out
	 mov	1, %g1
	lduw	[%o0], %g2
	subcc	%g2, 1, %g7
	be,pn	%icc, loop2
	 nop
	membar	#StoreStore | #LoadStore
	stb	%g0, [%o1]
#ifdef CONFIG_PREEMPT
	ldsw	[%g6 + TI_PRE_COUNT], %g3
	sub	%g3, 1, %g3
	stw	%g3, [%g6 + TI_PRE_COUNT]
#endif

	b,pt	%xcc, nzero
	 nop
spin_on_lock:
	ldub	[%o1], %g3
	brnz,pt	%g3, spin_on_lock
	 membar	#LoadLoad
	ba,pt	%xcc, to_zero
	 nop
	nop